/*******************************************************************************
 * Copyright 2011-2012 Dik Grapendaal
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package sh.grapendaal.tsuushin.message;

import java.util.Arrays;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

/**
 * This class is the base class for any message that could come in from the IRC
 * server. It contains the basic parsing logics for the setup of most incoming
 * IRC messages.
 */
public abstract class IncomingMessage {
	private String raw;
	private String[] chunks = null;
	private MessageType messageType;
	private User user;
	private String content;

	/**
	 * Default no argument constructor, in case the message type gets determined
	 * later on in processing the message.
	 */
	public IncomingMessage() {
	}

	/**
	 * This constructor must be called from extending classes. It sets the
	 * message type of this IncomingMessage instance.
	 * 
	 * @param messageType
	 *            The message type this instance represents.
	 */
	protected IncomingMessage(MessageType messageType) {
		this.messageType = messageType;
	}

	/**
	 * Returns the raw message this IncomingMessage instance represents.
	 * 
	 * @return The raw message as it was received.
	 */
	public String getRaw() {
		return raw;
	}

	/**
	 * Sets the raw message this IncomingMessage represents.
	 * 
	 * @param raw
	 *            The raw message as it was received.
	 */
	protected void setRaw(String raw) {
		this.raw = raw;
		this.setChunks(this.getRaw().split(" "));
	}

	/**
	 * Returns the array with the raw message split in chunks. Useful for
	 * extracting data out of responses.
	 * 
	 * @return The raw message split in chunks, usually split by spaces.
	 */
	public String[] getChunks() {
		return Arrays.copyOf(this.chunks, this.chunks.length);
	}

	/**
	 * Sets the array with the raw message split in chunks.
	 * 
	 * @param chunks
	 *            The raw message split in chunks.
	 */
	protected void setChunks(String[] chunks) {
		this.chunks = Arrays.copyOf(chunks, chunks.length);
	}

	/**
	 * Returns the type of this IncomingMessage instance.
	 * 
	 * @return The message type.
	 */
	public MessageType getMessageType() {
		return messageType;
	}

	/**
	 * Sets the message type of this IncomingMessage instance. This method is
	 * provided for messages that determine their exact type during parsing.
	 * 
	 * @param messageType
	 *            The message type to set.
	 */
	protected void setMessageType(MessageType messageType) {
		this.messageType = messageType;
	}

	/**
	 * Returns the User object associated with this message which contains
	 * information about the user who is responsible for this message.
	 * 
	 * @return The User object.
	 */
	public User getUser() {
		return user;
	}

	/**
	 * Sets the User object for this message.
	 * 
	 * @param user
	 *            The User object to set.
	 */
	private void setUser(User user) {
		this.user = user;
	}

	/**
	 * Returns the content of this message.
	 * 
	 * @return The content of the message.
	 */
	public String getContent() {
		return content;
	}

	/**
	 * Sets the content of the message, often a message the user can specify,
	 * such as a join/part/quit message or the actual message of a private
	 * message or notice.
	 * 
	 * @param content
	 *            The content of the message.
	 */
	public void setContent(String content) {
		this.content = content;
	}

	/**
	 * Parses the given message for useful information and stores it in the
	 * class. Will try to parse the first chunk of a received IRC message into a
	 * User object, but also look if there's actual content in this message. If
	 * so, stores that in the content property.
	 * 
	 * @param message
	 *            The received message to parse.
	 */
	public void parse(String message) {
		this.setRaw(message);

		if (message.charAt(0) == ':') {
			String userString = this.getChunks()[0].substring(1);
			this.setUser(User.parse(userString));
		}

		int contentPosition = message.indexOf(" :");
		if (contentPosition != -1) {
			this.setContent(message.substring(contentPosition + 2));
		}
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(this.chunks).append(this.content).append(this.messageType).append(this.raw)
				.append(this.user).toHashCode();
	}

	@Override
	public boolean equals(Object object) {
		if (!(object instanceof IncomingMessage)) {
			return false;
		}
		if (this == object) {
			return true;
		}
		IncomingMessage rhs = (IncomingMessage) object;
		return new EqualsBuilder().append(this.chunks, rhs.chunks).append(this.content, rhs.content)
				.append(this.messageType, rhs.messageType).append(this.raw, rhs.raw).append(this.user, rhs.user)
				.isEquals();
	}

	@Override
	public String toString() {
		return new ToStringBuilder(this).append(this.chunks).append(this.content).append(this.messageType)
				.append(this.raw).append(this.user).toString();
	}
}
